import path from 'path'
import os from 'os'
import * as uuid from 'uuid'
import fs from 'fs'
import log from '../logging'
import platform from '../platform'
import * as download from './download'
import * as cache from './cache'
import * as extract from './extract'
import * as which from './which'
import logging from '../logging'
import process from 'process'
import exec from '../exec'

export const flowToolRegistryServerPort = 11516
export async function install(
    toolName: string,
    version: string,
    toolSuffix?: string
): Promise<string> {
    const toolRegistryUrl = process.env['FLOW_TOOL_REGISTRY_URL']
    if(!toolRegistryUrl) {
       throw new Error('env var "FLOW_TOOL_REGISTRY_URL" must be set')
    }
    const existCachedDir = await which.which(toolName, version)
    if(!!existCachedDir) {
        log.info(`Found cached tool ${toolName} ${version} in ${existCachedDir}`)
        return Promise.resolve(existCachedDir)
    }
    const copied = await tryCopyFromLocalCache(toolName, version)
    if (copied) {
        const existCachedDir = await which.which(toolName, version)
        log.info(`Copied ${toolName} ${version} from local cache server. Path is ${existCachedDir}`)
        return Promise.resolve(existCachedDir)
    }
    const downloadUrl =
        `${toolRegistryUrl}/${toolName}/${toolName}-${version}-${platform.getPlatform()}-${platform.getArch()}` +
        (toolSuffix ? `.${toolSuffix}` : '')
    logging.info(`Downloading from ${downloadUrl}`)
    const downloadedTmpFilePath = await download.download(downloadUrl, {})
    logging.info(`Downloaded to ${downloadedTmpFilePath}`)
    const cachedDir = await cacheExtractedTool(downloadedTmpFilePath, toolName, version, toolSuffix)
    fs.rmSync(downloadedTmpFilePath)
    return Promise.resolve(cachedDir);
}

async function cacheExtractedTool(downloadedTmpFilePath: string, toolName: string, version: string, toolSuffix?: string): Promise<string> {
    if (toolSuffix === 'tar.gz' || toolSuffix === 'zip') {
        const extractedDir = path.join(os.tmpdir(), uuid.v4())
        logging.info(`Extracting ${downloadedTmpFilePath} to ${extractedDir}`)
        
        // 公共构建集群操作系统为 linux，尝试先使用 tar 解压
        if (process.env['NODEGROUP_TYPE'] === 'K8S' && toolSuffix === 'tar.gz') {
            const tarCommand = `mkdir -p ${extractedDir}; tar --strip-components=1 -zxvf ${downloadedTmpFilePath} -C ${extractedDir} --warning=no-unknown-keyword >/dev/null`
            logging.info(`Try to exec: /bin/bash -c ${tarCommand}`)
            const resultCode = await exec.call('/bin/bash', ['-c', tarCommand], {
                listener: {
                    stdoutLine: (line: string) => log.info(line),
                    stderrLine: (line: string) => log.warning(line)
                }
            })

            if (resultCode !== 0) {
                logging.error('Failed to use tar to extract, fallback to use decompress instead.')
                await extract.extract(downloadedTmpFilePath, extractedDir, {
                    strip: 1
                })
            }
        } else {
            // 私有构建集群有缓存机制，直接使用 decompress 解压
            await extract.extract(downloadedTmpFilePath, extractedDir, {
                strip: 1
            })
        }

        logging.info(`Caching ${extractedDir}`)
        const cachedDir = await cache.cacheDir(extractedDir, toolName, version)
        logging.info(`Cached ${extractedDir} to ${cachedDir}`)
        fs.rmSync(extractedDir, {recursive: true, force: true})
        return Promise.resolve(cachedDir)
    }
    logging.info(`Caching ${downloadedTmpFilePath}`)
    const cachedDir = await cache.cacheFile(downloadedTmpFilePath, `${toolName}-${version}`, toolName, version)
    logging.info(`Cached ${downloadedTmpFilePath} to ${cachedDir}`)
    return Promise.resolve(cachedDir)
}

async function tryCopyFromLocalCache(
    toolName: string,
    version: string
): Promise<boolean> {
    const cpToolUrl = `http://localhost:${flowToolRegistryServerPort}/restore?toolname=${toolName}&version=${version}&arch=${platform.getArch()}`
    const timeoutMillSeconds = 10 * 1000
    try {
        const result = await download.requestURL(cpToolUrl, timeoutMillSeconds)
        log.info(`Get result from tool registry sidecar: ${result}`)
        return Promise.resolve(true)
    } catch (error) {
        if (`${error}`.includes('ECONNREFUSED')) {
            // tool registry sidecar is not ready, skip cp from tool registry sidecar operation
        } else {
            log.warning(`Get result from tool registry sidecar failed, ${error}`)
        }
        return Promise.resolve(false)
    }
}
